/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.udt.designtime.manager.services;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.inspur.edp.bef.bizentity.GspBizEntityElement;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.collection.GspAssociationCollection;
import com.inspur.edp.cef.designtime.api.collection.GspAssociationKeyCollection;
import com.inspur.edp.cef.designtime.api.collection.GspFieldCollection;
import com.inspur.edp.cef.designtime.api.element.*;
import com.inspur.edp.cef.designtime.api.entity.GspCommonField;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.das.commonmodel.collection.GspElementCollection;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.service.MetadataRTService;
import com.inspur.edp.lcm.metadata.api.service.MetadataService;
import com.inspur.edp.lcm.metadata.api.service.RefCommonService;
import com.inspur.edp.udt.designtime.api.converter.UnifiedDataTypeDefConverter;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.extension.BaseUdtExtension;
import com.inspur.edp.udt.designtime.api.extension.BaseUdtExtensionDeserializer;
import com.inspur.edp.udt.designtime.api.extension.UdtExtensionConfig;
import com.inspur.edp.udt.designtime.api.extension.UdtExtensionConfigs;
import com.inspur.edp.udt.designtime.api.json.SimpleDataTypeDeserializer;
import com.inspur.edp.udt.designtime.api.json.SimpleDataTypeSerializer;
import com.inspur.edp.udt.designtime.api.nocode.BusinessField;
import com.inspur.edp.udt.designtime.api.nocode.FiledAssoInfo;
import com.inspur.edp.udt.designtime.api.nocode.IBusinessFieldService;
import com.inspur.edp.udt.designtime.api.nocode.RefField;
import com.inspur.edp.udt.designtime.manager.ContentSerializer;
import com.inspur.edp.udt.designtime.manager.repository.BusinessFieldRepository;
import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 业务字段服务
 */
public class BusinessFieldService implements IBusinessFieldService {
    private BusinessFieldRepository repository;

    public BusinessFieldService(BusinessFieldRepository businessFieldRepository){
        this.repository = businessFieldRepository;
    }

    @Override
    public List<BusinessField> getBusinessFields(){
        List<BusinessField> businessFields = this.repository.findAll();
        return businessFields;
    }

    @Override
    public List<BusinessField> getBusinessFieldsByCategoryId(String categoryId) {
        List<BusinessField> businessFields = this.repository.findByCategoryId(categoryId);
        if(businessFields!=null && businessFields.size()>0){
            for(BusinessField businessField: businessFields){
                businessField.setAssoInfo(new FiledAssoInfo());
            }
        }
        return businessFields;
    }

    @Override
    public BusinessField getBusinessField(String id) {
        BusinessField businessField = null;
        businessField = this.repository.findById(id).orElse(null);
        if(businessField != null){
//            UnifiedDataTypeDefConverter converter = new UnifiedDataTypeDefConverter();
            ContentSerializer contentSerializer = new ContentSerializer();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = null;
            try {
                jsonNode = objectMapper.readTree(businessField.getContent());
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            businessField.setUnifiedDataTypeDef((UnifiedDataTypeDef) contentSerializer.DeSerialize(jsonNode));
            businessField.setAssoInfo(new FiledAssoInfo());
        }
        return businessField;
    }

    public void saveBusinessField(BusinessField businessField){
        BusinessField temp = null;
        if(businessField.getId() == null || businessField.getId().length() == 0){
            businessField.setId(UUID.randomUUID().toString());
        }
        else {
            temp = getBusinessField(businessField.getId());
        }
        businessField.setUnifiedDataTypeDef(getSimpleAssoUDT(businessField, "Association"));
//        UnifiedDataTypeDefConverter converter = new UnifiedDataTypeDefConverter();
        ContentSerializer contentSerializer = new ContentSerializer();
        businessField.setLastChangeBy(CAFContext.current.getUserId());
        businessField.setLastChangeOn(new Date());
        businessField.setContent(contentSerializer.Serialize(businessField.getUnifiedDataTypeDef()).toString());
        this.repository.save(businessField);
    }

    private UnifiedDataTypeDef getSimpleAssoUDT(BusinessField businessField, String type){
        SimpleDataTypeDef udt = new SimpleDataTypeDef();
        handleBasicUdtInfo(udt, businessField);
        //每次都是重新搞
        for (Map.Entry<String, String> entry: businessField.getUdtExtensions().entrySet()){
            String extendType = entry.getKey();
            UdtExtensionConfig config = UdtExtensionConfigs.getInstance().getExtensionConfig(extendType);
            try {
                BaseUdtExtensionDeserializer deserializer = (BaseUdtExtensionDeserializer) Class.forName(config.getDeserclass()).newInstance();
                ObjectMapper mapper = new ObjectMapper();
                SimpleModule module = new SimpleModule();
                module.addDeserializer(BaseUdtExtension.class, deserializer);

                mapper.registerModule(module);
                udt.getUdtExtensions().put(extendType, (BaseUdtExtension) mapper.readValue(entry.getValue(), BaseUdtExtension.class));
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        if(type.equals("Association")){
            handleAssociationInfo(udt, businessField);
        }
        else if(type.equals("Enum")){

        }
        else if(type.equals("Normal")){
            handleEnumInfo(udt, businessField);
        }
        return udt;
    }

    /**
     * 处理基本信息
     * @param udt
     * @param businessField
     */
    private void handleBasicUdtInfo(SimpleDataTypeDef udt, BusinessField businessField){
        udt.setID(UUID.randomUUID().toString());
        udt.setCode(businessField.getCode());
        udt.setName(businessField.getName());
        // todo cef runtime 引用
//        udt.setCreatedDate(Date.from(CAFContext.current.getCurrentDateTime().toInstant()));
//        udt.setModifiedDate(Date.from(CAFContext.current.getCurrentDateTime().toInstant()));
        udt.setMDataType(GspElementDataType.String);
        udt.setLength(36);
        udt.setEnableRtrim(true);
        udt.setEnumIndexType(EnumIndexType.Integer);
    }

    private void handleAssociationInfo(SimpleDataTypeDef udt, BusinessField businessField){
        udt.setObjectType(GspElementObjectType.Association);
        udt.setChildAssociations(new GspAssociationCollection());
        GspAssociation gspAssociation = new GspAssociation();
        GspMetadata metadata = getMetadataService().getMetadata(businessField.getAssoInfo().getRefModelID());
        GspBusinessEntity gspBusinessEntity = (GspBusinessEntity) metadata.getContent();
        IGspCommonObject refObject = gspBusinessEntity.findObjectById(businessField.getAssoInfo().getRefObjectID());

        gspAssociation.setId(UUID.randomUUID().toString());
        gspAssociation.setRefModelID(businessField.getAssoInfo().getRefModelID());
        gspAssociation.setRefModelCode(gspBusinessEntity.getCode());
        gspAssociation.setRefModelName(gspBusinessEntity.getName());
        gspAssociation.setRefObjectCode(refObject.getCode());
        gspAssociation.setRefObjectID(refObject.getID());
        gspAssociation.setRefObjectName(refObject.getName());

        GspAssociationKeyCollection keyCollection = new GspAssociationKeyCollection();
        GspAssociationKey associationKey = new GspAssociationKey();
        associationKey.setSourceElement(gspBusinessEntity.findObjectById(gspAssociation.getRefObjectID()).getIDElement().getID());
        associationKey.setSourceElementDisplay(gspBusinessEntity.findObjectById(gspAssociation.getRefObjectID()).getIDElement().getLabelID());
        associationKey.setTargetElement(businessField.getCode());
        associationKey.setTargetElementDisplay("");
        keyCollection.addAssociation(associationKey);
        gspAssociation.setKeyCollection(keyCollection);

        gspAssociation.setRefElementCollection(new GspFieldCollection());
        if(businessField.getAssoInfo().getElements() != null && businessField.getAssoInfo().getElements().size() >0){
            for(RefField field: businessField.getAssoInfo().getElements()){
                GspCommonField commonField = new GspCommonField();
                commonField.setID(field.getId());
                IGspCommonElement commonElement = gspBusinessEntity.findElementById(field.getRefElementId());
                commonField.setLabelID(businessField.getCode() + "_" + commonElement.getLabelID());
                commonField.setCode(commonElement.getCode());
                commonField.setName(commonElement.getName());
                commonField.setMDataType(commonElement.getMDataType());
                commonField.setLength(commonElement.getLength());
                commonField.setIsRefElement(true);
                commonField.setEnableRtrim(false);
                commonField.setRefElementId(field.getRefElementId());
                commonField.setParentAssociation(gspAssociation);
                commonField.setUdtID(commonElement.getUdtID());
                commonField.setUdtName(commonElement.getUdtName());
                commonField.setIsUdt(commonElement.getIsUdt());
                if(commonField.getIsUdt()){//带出字段设置一下
                    if(commonElement.getChildElements() != null && commonElement.getChildElements().size() >0){
                        for(IGspCommonField childField: commonElement.getChildElements()){
                            GspBizEntityElement commonChildField = new GspBizEntityElement();
                            commonChildField.setID(childField.getID());
                            commonChildField.setLabelID(childField.getLabelID());
                            commonChildField.setIsUdt(childField.getIsUdt());
                            commonChildField.setCode(childField.getCode());
                            commonChildField.setName(childField.getName());
                            if(childField instanceof GspBizEntityElement){
                                commonChildField.setColumnID(((GspBizEntityElement)childField).getColumnID());
                            }
                            commonChildField.setEnableRtrim(childField.isEnableRtrim());
                            commonChildField.setMDataType(childField.getMDataType());
                            commonChildField.setLength(childField.getLength());
                            commonField.getChildElements().add(commonChildField);
                        }
                    }
                }
                gspAssociation.getRefElementCollection().add(commonField);
            }
        }
        AssoModelInfo assoModelInfo = new AssoModelInfo();
        assoModelInfo.setModelConfigId(gspBusinessEntity.getGeneratedConfigID());
        assoModelInfo.setMainObjCode(gspBusinessEntity.getMainObject().getCode());
        gspAssociation.setAssoModelInfo(assoModelInfo);
        udt.getChildAssociations().add(gspAssociation);
    }

    private void handleEnumInfo(SimpleDataTypeDef udt, BusinessField businessField){
        udt.setObjectType(GspElementObjectType.Enum);
        udt.setChildAssociations(new GspAssociationCollection());
        GspAssociation gspAssociation = new GspAssociation();
        GspMetadata metadata = getMetadataService().getMetadata(businessField.getAssoInfo().getRefModelID());
        GspBusinessEntity gspBusinessEntity = (GspBusinessEntity) metadata.getContent();
        IGspCommonObject refObject = gspBusinessEntity.findObjectById(businessField.getAssoInfo().getRefObjectID());

        gspAssociation.setId(UUID.randomUUID().toString());
        gspAssociation.setRefModelID(businessField.getAssoInfo().getRefModelID());
        gspAssociation.setRefModelCode(gspBusinessEntity.getCode());
        gspAssociation.setRefModelName(gspBusinessEntity.getName());
        gspAssociation.setRefObjectCode(refObject.getCode());
        gspAssociation.setRefObjectID(refObject.getID());
        gspAssociation.setRefObjectName(refObject.getName());

        GspAssociationKeyCollection keyCollection = new GspAssociationKeyCollection();
        GspAssociationKey associationKey = new GspAssociationKey();
        associationKey.setSourceElement(gspBusinessEntity.findObjectById(gspAssociation.getRefObjectID()).getIDElement().getID());
        associationKey.setSourceElementDisplay(gspBusinessEntity.findObjectById(gspAssociation.getRefObjectID()).getIDElement().getLabelID());
        associationKey.setTargetElement(businessField.getCode());
        associationKey.setTargetElementDisplay("");
        keyCollection.addAssociation(associationKey);
        gspAssociation.setKeyCollection(keyCollection);

        gspAssociation.setRefElementCollection(new GspFieldCollection());
        if(businessField.getAssoInfo().getElements() != null && businessField.getAssoInfo().getElements().size() >0){
            for(RefField field: businessField.getAssoInfo().getElements()){
                GspCommonField commonField = new GspCommonField();
                commonField.setID(field.getId());
                IGspCommonElement commonElement = gspBusinessEntity.findElementById(field.getRefElementId());
                commonField.setLabelID(businessField.getCode() + "_" + commonElement.getLabelID());
                commonField.setCode(commonElement.getCode());
                commonField.setName(commonElement.getName());
                commonField.setMDataType(commonElement.getMDataType());
                commonField.setLength(commonElement.getLength());
                commonField.setIsRefElement(true);
                commonField.setEnableRtrim(false);
                commonField.setRefElementId(field.getRefElementId());
                commonField.setParentAssociation(gspAssociation);
                gspAssociation.getRefElementCollection().add(commonField);
            }
        }
        AssoModelInfo assoModelInfo = new AssoModelInfo();
        assoModelInfo.setModelConfigId(gspBusinessEntity.getGeneratedConfigID());
        assoModelInfo.setMainObjCode(gspBusinessEntity.getMainObject().getCode());
        gspAssociation.setAssoModelInfo(assoModelInfo);
        udt.getChildAssociations().add(gspAssociation);
    }

    private com.inspur.edp.lcm.metadata.api.service.MetadataRTService metadataService;
    private MetadataRTService getMetadataService(){
        if(metadataService==null)
            metadataService= SpringBeanUtils.getBean(MetadataRTService.class);
        return metadataService;
    }

    public JsonNode getNodeFromJson(String json) {

        ObjectMapper mapper = new ObjectMapper();
        JsonNode node = null;

        try {
            node = mapper.readTree(json);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("json串解析失败！");
        }
        return node;
    }
}
